#include "soft_i2c.h"
#include "delay.h"

#define I2C_DELAY()     soft_i2c_delay()

typedef struct soft_i2c
{
    /*0: SCL 1:SDA */
    GPIO_TypeDef* port[2];
    uint16_t pin[2];
    uint8_t slave_addr;
    uint8_t busy;
}soft_i2c_t;

static void soft_i2c_delay(void)
{
    // volatile uint32_t i = 0x20;
    // while(--i);
}

soft_i2c_t soft_i2c[_SOFT_I2C_MAX] = {
    /*I2C1*/
    {
        {GPIOB, GPIOB},
        {GPIO_PIN_8, GPIO_PIN_9},
        0,0
    },
    /*I2C2*/
    {
        {GPIOB, GPIOB},
        {GPIO_PIN_6, GPIO_PIN_7},
        0,0
    }
};

/*
*********************************************************************************************************
*	函 数 名: soft_i2c_wait_ack
*	功能说明: CPU产生一个时钟，并读取器件的ACK应答信号
*	形    参：无
*	返 回 值: 返回0表示正确应答，1表示无器件响应
*********************************************************************************************************
*/
static uint8_t soft_i2c_wait_ack(uint8_t  id)
{
	uint8_t re;
	SOFT_I2C_SDA(id, 1);	/* CPU释放SDA总线 */
	I2C_DELAY();
	SOFT_I2C_SCL(id, 1);	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	I2C_DELAY();
	if (SOFT_I2C_READ(id))	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	SOFT_I2C_SCL(id, 0);
	I2C_DELAY();
	return re;
}

/*
*********************************************************************************************************
*	函 数 名: soft_i2c_ack
*	功能说明: CPU产生一个ACK信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void soft_i2c_ack(uint8_t  id)
{
	SOFT_I2C_SDA(id, 0);	/* CPU驱动SDA = 0 */
	I2C_DELAY();
	SOFT_I2C_SCL(id, 1);	/* CPU产生1个时钟 */
	I2C_DELAY();
	SOFT_I2C_SCL(id, 0);
	I2C_DELAY();
	SOFT_I2C_SDA(id, 1);	/* CPU释放SDA总线 */
}

/*
*********************************************************************************************************
*	函 数 名: soft_i2c_nack
*	功能说明: CPU产生1个NACK信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void soft_i2c_nack(uint8_t  id)
{
	SOFT_I2C_SDA(id, 1);	/* CPU驱动SDA = 1 */
	I2C_DELAY();
	SOFT_I2C_SCL(id, 1);	/* CPU产生1个时钟 */
	I2C_DELAY();
	SOFT_I2C_SCL(id, 0);
	I2C_DELAY();	
}

/*******************************************************/
/*
*********************************************************************************************************
*	函 数 名: soft_i2c_start
*	功能说明: CPU发起I2C总线启动信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void soft_i2c_send_start(uint8_t id)
{
	/* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
    /* upg_mark 后续需要在发送起始信号时检测是否有总线使用权*/
	SOFT_I2C_SDA(id, 1);

    SOFT_I2C_SCL(id, 1);
	I2C_DELAY();

	SOFT_I2C_SDA(id, 0);
	I2C_DELAY();

	SOFT_I2C_SCL(id, 0);
	I2C_DELAY();
}
/*
*********************************************************************************************************
*	函 数 名: soft_i2c_stop
*	功能说明: CPU发起I2C总线停止信号
*	形    参：无
*	返 回 值: 无
*********************************************************************************************************
*/
static void soft_i2c_send_stop(uint8_t id)
{
	/* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */
	SOFT_I2C_SDA(id, 0);

	SOFT_I2C_SCL(id, 1);
	I2C_DELAY();

	SOFT_I2C_SDA(id, 1);
    I2C_DELAY();
}

/*
*********************************************************************************************************
*	函 数 名: soft_i2c_send_byte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参：_ucByte ： 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
static void soft_i2c_send_byte(uint8_t id,uint8_t _ucByte)
{
	uint8_t i;
	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{
        uint8_t bit = (_ucByte & 0x80) != 0;
        SOFT_I2C_SDA(id, bit);
		I2C_DELAY();

		SOFT_I2C_SCL(id, 1);
		I2C_DELAY();
        	
		SOFT_I2C_SCL(id, 0);
        I2C_DELAY();

		if (i == 7)
		{
			SOFT_I2C_SDA(id, 1); // 释放总线
		}
		_ucByte <<= 1;	/* 左移一个bit */
	}
}

/*
*********************************************************************************************************
*	函 数 名: soft_i2c_read_byte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参：无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
static uint8_t soft_i2c_read_byte(uint8_t id)
{
	uint8_t i;
	uint8_t value;
	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;

		SOFT_I2C_SCL(id, 1);
		I2C_DELAY();

        value &= ~(1);
        value |= SOFT_I2C_READ(id);

		SOFT_I2C_SCL(id, 0);
		I2C_DELAY();
	}
	return value;
}

static void soft_i2c_rcc_en(uint8_t id)
{
    switch(id)
    {
        case SOFT_I2C_1:
            __HAL_RCC_GPIOB_CLK_ENABLE();
            break;
        case SOFT_I2C_2:
            __HAL_RCC_GPIOB_CLK_ENABLE();
            break;
        default:
            return false;
    }
    return true;
}

/*******************************************************/

void soft_i2c_start(uint8_t id, uint8_t addr)
{
    if(IS_SOFT_ID(id) && !soft_i2c[id].busy)
    {
        soft_i2c[id].slave_addr = addr<<1;
        soft_i2c_send_start(id);
    }
}

void soft_i2c_stop(uint8_t id)
{
    if(IS_SOFT_ID(id) && !soft_i2c[id].busy )
    {
        soft_i2c_send_stop(id);
    }
}

uint8_t soft_i2c_write(uint8_t id, uint8_t reg, uint8_t *buf, uint8_t len)
{
    uint8_t i;
    if(IS_SOFT_ID(id) && !soft_i2c[id].busy)
    {

        soft_i2c[id].busy = 1;
        soft_i2c_send_byte(id, soft_i2c[id].slave_addr|I2C_WR);
        if(soft_i2c_wait_ack(id))
        {
            goto fail;
        }
        
        soft_i2c_send_byte(id, reg);
        if(soft_i2c_wait_ack(id))
        {
            goto fail;
        }
        
        for(i=0; i<len; i++)
        {
            soft_i2c_send_byte(id, buf[i]);
            if(soft_i2c_wait_ack(id))
            {
                break;
            }
        }
        soft_i2c[id].busy = 0;
        return i;
    }
fail:
    soft_i2c[id].busy = 0;
    return 0;
}

uint8_t soft_i2c_read(uint8_t id, uint8_t reg, uint8_t *buf, uint8_t len)
{
    if(IS_SOFT_ID(id) && !soft_i2c[id].busy)
    {
        uint8_t i;
        soft_i2c[id].busy = 1;
        soft_i2c_send_byte(id, soft_i2c[id].slave_addr|I2C_WR);
        if(soft_i2c_wait_ack(id))
        {
            goto fail;
        }
        soft_i2c_send_byte(id, reg);
        if(soft_i2c_wait_ack(id))
        {
            goto fail;
        }
        
        soft_i2c_send_start(id);
        soft_i2c_send_byte(id, soft_i2c[id].slave_addr|I2C_RD);
        if(soft_i2c_wait_ack(id))
        {
            goto fail;
        }
        
        for(i=0; i<len-1; i++)
        {
            buf[i] = soft_i2c_read_byte(id);
            soft_i2c_ack(id);
        }
        buf[i] = soft_i2c_read_byte(id);
        soft_i2c_nack(id);
        soft_i2c[id].busy = 0;
        return i+1;
    }
fail:
    soft_i2c[id].busy = 0;
    return 0;
}

bool soft_i2c_init(uint8_t id)
{
    if(IS_SOFT_ID(id))
    {
        GPIO_InitTypeDef GPIO_InitStruct = {0};

        soft_i2c_rcc_en(id);
        HAL_GPIO_WritePin(soft_i2c[id].port[0], soft_i2c[id].pin[0], GPIO_PIN_SET); /*初始化TFT PIN状态*/
        HAL_GPIO_WritePin(soft_i2c[id].port[1], soft_i2c[id].pin[1], GPIO_PIN_SET); /*初始化TFT PIN状态*/

        GPIO_InitStruct.Pin = soft_i2c[id].pin[0];
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        HAL_GPIO_Init(soft_i2c[id].port[0], &GPIO_InitStruct);

        GPIO_InitStruct.Pin = soft_i2c[id].pin[1];
        HAL_GPIO_Init(soft_i2c[id].port[1], &GPIO_InitStruct);

        soft_i2c[id].busy = 0;

        return true;
    }
    return false;
}
